/*
 * ES6 
 * 
 * 4-5 模板字符串
 * 传统的输出模板是这样 '<a>' + 'test' + '</a>'
 * es6则引入了模板字符串：
 *   `` ,这个反引号。
 * 
 * 如果需要在模板字符串中加入变量，则需要使用${},美元符号和花括号。花括号内，可以放入任意JavaScript表达式，
 * 可以进行运算，以及引用对象属性。更神奇的是，模板字符串中还能调用函数。
 * 
 * 7-8 指数运算符
 * ES2016新增一个指数运算符（**）
 * 多个指数运算符连用时，是从最右边开始计算的。如：
 *   2**3**2  结果为512，相当于是2的9次方。
 * Math.pow()
 * 
 * 8-4 函数的name属性
 * 返回函数的name值，这个属性早就被浏览器广泛支持，知道ES6才写入标准；ES6对这个属性的行为进行了一些修改。
 * 情况1：如果将一个匿名函数赋值给一个变量，ES5中的name属性，会返回空字符串，而ES6中的name属性会返回实际的函数名，
 *   let f = function （）{} ES5中name属性则返回 ‘’，ES6返回f。
 * 
 * 情况2：如果将一个具名函数赋值给一个变量，则两者都返回这个具名函数原本的名字。
 * 
 * 
 * 9-1 扩展运算符
 * 扩展运算符是三个点（...）。该运算符主要用于函数调用。注意，只有函数调用时，扩展运算符才可以放在圆括号中，
 * 否则会报错。
 * 
 * 扩展运算符还提供了复制数组的便捷写法。
 * 在ES5中，只能通过变通的办法来实现，如：const a1 = [1,2,3]; const a2 = a1.concat();
 * 这样一来，修改a2中的数据，就不会对a1产生影响。
 * 再来看看ES6的写法，const a1 = [1,2,3]; const a2 = [...a1]; 或者 const [...a2] = a1;
 * 这两种写法，a2都是a1的克隆。
 * 9-1-6 Map和Set数据结构
 * 具有Iterator接口的对象，使用扩展运算符，将报错。
 * 
 * 9-2 Array.from（）用于将两类对象转换成数组：类似数组的对象和可变历的对象（ES6新增的数据结构Set和Map）。
 *   let arrayLike = {
 * 	   '0': 'a',
 * 	   '1': 'b',
 * 	   '2': 'c'
 *   }
 *   ES5写法：
 *   var arr1 = [].slice.call.(arrayLike);
 *   ES6写法：
 *   let arr1 = Array.from(arrayLike);
 * 实际应用就是，DOM操作返回的nodelist集合，以及函数内部的arguments对象，应用Array.from()方法都可以将
 * 其转换为数组。
 * 一句话，凡是有length属性的对象，都可以通过Array.from()方法转换为数组，而扩展运算符则无法转换。
 * 
 * 9-9 数组实例的flat(),和flatMap()
 * Array.prototype.flat()用于将嵌套的数组“拉平”，即将多维数组变成一维数组。flat（）可以接收参数，即将
 * 目标数组变成几维（想要拉平的层数），如果使用Infinity关键字作为参数，则不管数组是多少维，都将变成一维数组。
 * 
 * 9-结尾 
 * 数组的遍历方法
 * foreach、filter、reduce、every、some、map等
 * 
 * 名值对：即键名和键值成对出现
 * 
 * 
 * 10-4 属性的可枚举性和遍历
 * 属性遍历的五种方法：
 *   1. for...in 循环遍历自身的和继承的可枚举属性（不含symbol属性）
 *   2. Object.keys（obj）返回一个数组，包括对象自身的（不含继承的）所有可枚举属性（不含symbol）的键名
 *   3. Object.getOwnPropertyNames（）
 *   4. Object.getOwnPropertySymbols（）
 *   5. reflect.ownKeys（）
 * 
 * 10-5 super关键字
 * this关键字，总是指向函数所在的当前对象。ES6又新增了另一个关键字super，指向当前对象的原型对象。
 * 
 * 13-1 Set
 * Set实际上是特殊的数组，其成员的值都是唯一的，没有重复的值。
 * Set本身是一个构造函数，用来生成Set数据结构。Set函数接受一个数组（或者具有iterable接口的其他数据结构）
 * 作为参数，用来初始化。
 * 
 * 去重：[...new Set(array)]
 * 
 * Map 也是hash结构，名值对结构，可以通过扩展运算符将map转为数组（...）
 * 
 * 16 Promise对象
 * 16-1 Promise的含义
 * Promise，简单来说，就是一个容器，里面保存着某个未来才会结束的事件的结果。从语法上来说，它是一个对象，并且
 * 可以获取异步操作的消息。有两特点：
 *   1、对象 的状态不受外界的影响。Promise对象代表一个异步操作，有三种状态：pending（进行中）、fulfilled
 *   （已完成）、rejected（已失败）。
 *   2、 一旦状态改变，就不会在变，任何时候都可以得到这个结果。
 *   const promise = new Promise（function（resolve，reject）{
 *     if （）{
 * 	      resolve（value）；
 *     } else {
 * 	      reject（error）；
 *     }
 *   }）
 *   Promise构造函数接受一个函数作为参数，该函数的两个参数分别是resolve和reject。它们是两个函数，由
 *   JavaScript引擎提供，不用自己部署。
 *   很显然，resolve函数的作用是，成功的调用，而reject则是失败后调用。
 *   Promise实例生成以后，可以用then方法分别制定resolved状态和rejected状态的回调函数。
 *   
 *   promise.then(function(value){ //success }, function(error){ //failure })
 *   从这里可以看出，then方法接收两个回调函数作为参数。第一个是成功后回调，第二个是失败后回调。这个两个
 *   函数都接受Promise对象传出的值作为参数。
 *   function timeOut(ms){
 * 	   return function( (resolve, reject) => {
 * 	     setTimeout(resolve, ms, "done");
 *     } ){}
 *   }
 *   timeOut(100).then( (value) => {
 * 	   console.log(value);
 *   })
 * 
 *   Promise新建立后立即执行，所以首先输出的是Promise。然后，then方法指向的回调函数，将在当前脚本所有
 *   同步任务执行完才会执行。
 * 
 *   16-3 Promise实例具有then方法，也就是说，then方法是定义在圆形对象Promise.prototype上的。它的作用
 *   是为Promise实例添加状态改变时的回调函数。
 *   
 *   16-4 Promise.prototype.catch()
 *   一般来说，不要在then方法里面定义reject状态的回调函数，即then的第二个参数，总是使用catch方法。
 *   promise.then（function(data){//...}）.catch(function(error){//...})
 *   与try...catch语句块不同的是，即使promise的catch不写，也不会影响到promise外层的代码，
 * 
 *   16-6 Promise.all（）
 *   Promise.all()方法，用于将多个Promise实例，包装成一个新的Promise实例。
 *   如 const P = Promise.all([p1,p2,p3]);
 *   上的代码中，Promise.all（）方法接受一个数组作为参数，如果不是，就会调用下面讲到的Promise.resolve方法，
 *   将参数转为Promise的实例，在进一步处理。
 *   P的状态由p1,p2,p3决定，分成两种情况：
 *      第一种、只有p1,p2,p3的状态都变成fullfilled，P的状态才会变成fullfilled，此时p1,p2,p3的返回值
 *             组成一个数组，传递给P的回调函数。
 *      第二种、只要p1,p2,p3中有一个被rejected，P的状态就会变成rejected，此时第一个被reject的实例的返回
 *             值会传递给P的回调函数。
 * 
 *   写一个图片加载的实例：
 *      const preloadImage = function(path){
 * 	      return new Promise(function(resolve, reject){
 * 	        const image = new Image();
 * 	        image.onload = resolve;
 * 	        image.onerror = reject;
 * 	        image.src = path;
 *        })
 *      }
 * 
 * 
 * 17-7 for...of循环
 *   ES6借鉴了C++,Java，C#，Python语言，引入for...of循环，作为遍历所有数据结构的统一方法。
 *   一个数据结构只要部署了Symbol.iterator属性，就被视为具有iterator接口，就可以用for...of来遍历它的
 *   成员。也就是说，for...of循环内部调用的是数据结构的Symbol.iterator方法。
 * 
 *   数组的键名是数字，但是for...in遍历是一字符串作为键名’0’，‘1’等等；实际上for...in主要为遍历对象而设计
 *   不同于foreach，它可以与break，continue和return配合使用。
 * 
 * 18-1 Generator的定义
 *   Generator函数是ES6提供的一种异步编程的解决方案。但是，语法行为与传统的函数完全不同。
 *   Generator函数有两个特点：1. function关键字与函数名之间有一个星号*；2. 函数体内部使用yield表达式，
 *     定义不同的yield表达式，定义不同的内部状态。
 *     看一个实例：
 *        function* helloWordGenerator（）{
 * 	        yield hello；
 *          yield world；
 *          return ‘ending’；
 *        }
 *        var hw = helloWordGenerator（）；
 *        函数内部有两个yield表达式和一个return语句，即它有三个状态。
 * 
 * 19. Generator函数的异步应用
 *   19-1 传统方法实现异步
 *     ES6之前，有四种方式实现异步编程：
 *       1. 回调函数；
 *       2. 事件监听；
 *       3. 发布/订阅；
 *       4. Promise；
 * 
 * 23. Module
 *   23-1 概述
 *     ES6的设计思想就是尽量静态化，使得在编译时就能确定模块的依赖关系，以及输入输出的变量。而Common.js和AMD
 *     都只能在运行时确定这些东西。如
 *        // common.js模块
 *        let (start, exists, readfile) = require('fs');
 *        // 等同于
 *        let _fs = require('fs');
 *        let start = fs.start;
 *        let exists = fs.exists;
 *        let readfile = fs.readfile;
 *     上面代码的实质就是整体加载fs模块（即加载fs的所有方法），生成一个对象（_fs）,然后再从这个对象中读取
 *     三个方法。这种加载称为运行时加载，因为只有运行时才能得到这个对象，导致完全没有办法在编译时就做“静态
 *     优化“。
 *     
 *     ES6模块不是对象，而是通过export命令显示指定输出的代码，再通过import命令引入。
 *       import { start, exists, readfile } from 'fs';
 *     上面的代码实质就是从fs模块加载3个方法，其他方法不加载。这种加载就称为“编译时加载”，“静态加载”。
 *     即ES6在编译时就完成了加载，效率要比前者的加载方式高。当然，这也就导致了没法引用ES6模块，因为
 *     它本身不是对象。
 *     
 *     ES6的模块自动采用了严格模式，不管是否在模块头部加上“use strict；”。
 *     严格模式有哪些限制：
 *     --变量只能先声明，再使用；
 *     --函数的参数不能有同名属性，否则报错；
 *     --不能使用前缀0表示八进制数，否则报错；等等... ...
 *     
 *     export输出的三种方式：
 *     -- export const a = 1；
 *     -- const a = 1; export {a};
 *     -- const a = 1; export {a as aa};
 *     
 *  23-5 模块的加载
 *     import { start, exists, readfile } from 'fs';
 *     import * as circle from 'fs';
 *     
 *  23-6 export default命令  
 *     在指定模块默认输出，加上export default命令，则在其他模块加载该模块时，import命令就可以为该模块指定
 *     任意名字，同时不需要使用大括号。下面看一组实例：
 *       export default function foo(){ console.log('foo'); }; // foo.js
 *       import foo from 'foo';
 *       export function foo(){ console.log('foo'); }; // foo.js
 *       import {foo} from 'foo';
 *     显然，一个模块只能有一个默认输出。
 *  23-10 import()
 *     import()返回的是一个Promise对象。下面是一个例子：
 *       import('fs.js').then( module => { module.loadPgeInto(XX)}).catch( error => {alert(1)})
 *     --import()按需加载，可以在需要的时候再加载某个模块；
 *     --条件加载。
 *       if (condition){ import('moduleA').then(...) } else {import('moduleB').then(...)};
 *     --动态模块路径      
 *       import(f()).then(...);
 *       根据函数f的返回结果，加载不同的模块。
 *     
 *     
 *     
 *     
 * */

